/* Includes -------------------------------------------------------------*/
#include "drv8301.h"


/* Private variables ----------------------------------------------------*/
static DRV8301_OBJ Drv8301 = DRV8301_DEFAULTS;


/* Private function prototypes ------------------------------------------*/
static void SPIB_Init(void);
static void Drv8301_Send(uint16_T TxData, uint16_T* RxData);
static void Drv8301_CalibrateDcOffset (uint16_T* IsenseU_Offset, uint16_T* IsenseV_Offset);


/* Private Functions ----------------------------------------------------*/
/*
 * Function name	: SPIB_Init
 * Description		:
 * Input			:
 * Output			:
 */
static void SPIB_Init (void)
{
#ifndef MATLAB_MEX_FILE
	EALLOW;
    
    /* Enable SPI-B peripheral clock */
    SysCtrlRegs.PCLKCR0.bit.SPIBENCLK 	= 1;
    
    /* Configure SPI-B pins */
    GpioCtrlRegs.GPAMUX2.bit.GPIO24  	= 3;
    GpioCtrlRegs.GPAMUX2.bit.GPIO25  	= 3;
    GpioCtrlRegs.GPAMUX2.bit.GPIO26  	= 3;
    GpioCtrlRegs.GPAMUX2.bit.GPIO27  	= 3;
    
    GpioCtrlRegs.GPAPUD.bit.GPIO24		= 0;        // 0=Enable, 1=Disable
    GpioCtrlRegs.GPAPUD.bit.GPIO25		= 0;        // 0=Enable, 1=Disable
    GpioCtrlRegs.GPAPUD.bit.GPIO26		= 0;        // 0=Enable, 1=Disable
    GpioCtrlRegs.GPAPUD.bit.GPIO27		= 0;        // 0=Enable, 1=Disable
    
    GpioCtrlRegs.GPAQSEL2.bit.GPIO24	= 3;        // Asynch input
    GpioCtrlRegs.GPAQSEL2.bit.GPIO25	= 3;        // Asynch input
    GpioCtrlRegs.GPAQSEL2.bit.GPIO26	= 3;        // Asynch input
    GpioCtrlRegs.GPAQSEL2.bit.GPIO27	= 3;        // Asynch input
    
    EDIS;
    
    SpibRegs.SPICCR.bit.SPISWRESET	  	= 0; 		// Initialize SPI operating flags and reset condition

	SpibRegs.SPICCR.bit.SPICHAR 	  	= 15;		// Set character length to 16 bits
	SpibRegs.SPICCR.bit.SPILBK        	= 0;		// Disable loopback
	SpibRegs.SPICCR.bit.CLKPOLARITY   	= 0;		// SPICLK is LOW during idle

	SpibRegs.SPICTL.bit.SPIINTENA	  	= 0;		// Disable interrupt
	SpibRegs.SPICTL.bit.TALK		  	= 1;		// Enable transmission
	SpibRegs.SPICTL.bit.MASTER_SLAVE  	= 1;  		// SPI Master mode
	SpibRegs.SPICTL.bit.CLK_PHASE	  	= 0;		// No delay on CLK signal
	SpibRegs.SPICTL.bit.OVERRUNINTENA 	= 0;		// Disable receiver overrun flag interrupt

	SpibRegs.SPIBRR 				  	= 17;		// SPI bit rate = LSPCLK / (SPIBRR + 1) = 90MHz / (17 + 1) = 5Mbps

	SpibRegs.SPIFFTX.bit.SPIFFENA		= 1;		// Enable SPI FIFO
	SpibRegs.SPIFFTX.bit.SPIRST			= 0;		// Reset SPI FIFO transmit and receive channels
	SpibRegs.SPIFFTX.bit.TXFFIENA		= 0;		// Disable Tx FIFO interrupt
	SpibRegs.SPIFFTX.bit.TXFIFO			= 0;		// Reset Tx FIFO pointer
	SpibRegs.SPIFFTX.bit.TXFFINTCLR		= 1;		// Clear Tx FIFO interrupt flag

	SpibRegs.SPIFFRX.bit.RXFFIENA		= 0;		// Disable Rx FIFO interrupt
	SpibRegs.SPIFFRX.bit.RXFIFORESET	= 0;		// Reset Rx FIFO pointer
	SpibRegs.SPIFFRX.bit.RXFFIL			= 2;		// Generate Rx interrupt when 2 words is received
	SpibRegs.SPIFFRX.bit.RXFFOVFCLR		= 1;		// Clear Rx FIFO overflow flag
	SpibRegs.SPIFFRX.bit.RXFFINTCLR		= 1;		// Clear Rx FIFO interrupt flag

	SpibRegs.SPIFFCT.bit.TXDLY			= 2;

	SpibRegs.SPIPRI.bit.TRIWIRE			= 0;		// Normal 4-wire SPI mode
	SpibRegs.SPIPRI.bit.STEINV			= 0;		// SPISTE is active LOW
	SpibRegs.SPIPRI.bit.FREE			= 1;		// Free run on emulation suspend

	SpibRegs.SPISTS.all				  	= 0x0000;	// Clear the status register
	SpibRegs.SPIFFRX.bit.RXFIFORESET	= 1;		// Re-enable Rx FIFO pointer
	SpibRegs.SPIFFTX.bit.TXFIFO			= 1;		// Re-enable Tx FIFO pointer
	SpibRegs.SPIFFTX.bit.SPIRST			= 1;		// Release SPI FIFO transmit and receive channels from reset
	SpibRegs.SPICCR.bit.SPISWRESET	  	= 1; 		// Release SPI module from software reset
#endif
}


/*
 * Function name	: Drv8301_Send
 * Description		: Send and Receive commands with DRV8301.
 * Input			:
 * Output			:
 */
static void Drv8301_Send(uint16_T TxData, uint16_T* RxData)
{
#ifndef MATLAB_MEX_FILE    
	Uint16 Dummy;

	/* Reset FIFO pointers */
	SpibRegs.SPIFFRX.bit.RXFIFORESET = 0;
	SpibRegs.SPIFFTX.bit.TXFIFO      = 0;
	SpibRegs.SPIFFRX.bit.RXFIFORESET = 1;
	SpibRegs.SPIFFTX.bit.TXFIFO		 = 1;

	/* Place the data to send to the TX FIFO */
	SpibRegs.SPITXBUF = (Uint16)TxData;
	SpibRegs.SPITXBUF = 0x0000;

	/* Wait until 2 words are received in the RX FIFO */
	while(SpibRegs.SPIFFRX.bit.RXFFST < 2);

	/* Read the 2 words from the RX FIFO */
	Dummy   = SpibRegs.SPIRXBUF;
	*RxData = (uint16_T)SpibRegs.SPIRXBUF;
#endif    
}


/*
 * Function name	: Drv8301_CalibrateDcOffset
 * Description		:
 * Input			:
 * Output			:
 */
static void Drv8301_CalibrateDcOffset (uint16_T* IsenseU_Offset, uint16_T* IsenseV_Offset)
{
#ifndef MATLAB_MEX_FILE    
	uint16_T IsenseU_Avg = 0;
	uint16_T IsenseV_Avg = 0;
    uint16_T i;

	/* Set DC_CAL pin HIGH */
	Drv8301_EnableDcCal();

	/* Add delay before measurement */
	DELAY_US(10000);

	for (i=0; i<10; i++)
	{
		/* Software force trigger SOC1 to convert Isense_U and Isense_V */
		AdcRegs.ADCSOCFRC1.bit.SOC0 = 1;

		/* Wait until ADC conversion is finished */
		while (AdcRegs.ADCCTL1.bit.ADCBSY);

		/* Sum the measured Isense values to take the average */
		IsenseU_Avg += (uint16_T)AdcResult.ADCRESULT0;
		IsenseV_Avg += (uint16_T)AdcResult.ADCRESULT1;
	}

	/* Convert the offset value in PU and save */
	*IsenseU_Offset = IsenseU_Avg / 10;
	*IsenseV_Offset = IsenseV_Avg / 10;

	/* Set DC_CAL pin LOW */
	Drv8301_DisableDcCal();

	/* Delay after the end of calibration process */
	DELAY_US(1000);
#endif
}


/* Public Functions -----------------------------------------------------*/
/*
 * Function name	: Drv8301_Init
 * Description		:
 * Input			:
 * Output			:
 */
void Drv8301_Init(void)
{
#ifndef MATLAB_MEX_FILE    
    /* Initialize SPIB to communicate with DRV8301 */
    SPIB_Init();

    /* Configure GPIOs for DRV8301 */
    EALLOW;
    GpioCtrlRegs.GPAMUX1.bit.GPIO13 = 0;    // OCTWn
    GpioCtrlRegs.GPAMUX1.bit.GPIO14 = 0;    // FAULTn
	GpioCtrlRegs.GPBMUX1.bit.GPIO43 = 0;	// DC_CAL
    GpioCtrlRegs.GPBMUX2.bit.GPIO51 = 0;	// EN_GATE
    
	GpioCtrlRegs.GPADIR.bit.GPIO13 = 0;	
	GpioCtrlRegs.GPADIR.bit.GPIO14 = 0;
    GpioCtrlRegs.GPBDIR.bit.GPIO43 = 1;
    GpioCtrlRegs.GPBDIR.bit.GPIO51 = 1;
    
    GpioCtrlRegs.GPAPUD.bit.GPIO13 = 0;
    GpioCtrlRegs.GPAPUD.bit.GPIO14 = 0;

    GpioDataRegs.GPBCLEAR.bit.GPIO43 = 1;
    GpioDataRegs.GPBCLEAR.bit.GPIO51 = 1;
    EDIS;
#endif    
}


/*
 * Function name	: Drv8301_EnableGate
 * Description		:
 * Input			:
 * Output			:
 */
void Drv8301_EnableGate(void)
{
#ifndef MATLAB_MEX_FILE
    GpioDataRegs.GPBSET.bit.GPIO51 = 1;
#endif
}


/*
 * Function name	: Drv8301_DisableGate
 * Description		:
 * Input			:
 * Output			:
 */
void Drv8301_DisableGate(void)
{
#ifndef MATLAB_MEX_FILE    
    GpioDataRegs.GPBCLEAR.bit.GPIO51 = 1;
#endif
}


/*
 * Function name	: Drv8301_FaultReset
 * Description		:
 * Input			:
 * Output			:
 */
void Drv8301_FaultReset(void)
{
#ifndef MATLAB_MEX_FILE    
	/* Disable the gate first */
	Drv8301_DisableGate();

	/* Delay for <10us to make sure DRV8301 to perform a fault reset */
	DELAY_US(5);

	/* Enable DRV8301 gate driver */
	Drv8301_EnableGate();

	/* Wait for DRV8301 to get ready. This IC needs 5ms-10ms to be stable after EN_GATE is set to HIGH */
	DELAY_US(7000);
#endif    
}


/*
 * Function name	: Drv8301_FullReset
 * Description		:
 * Input			:
 * Output			:
 */
void Drv8301_FullReset(void)
{
#ifndef MATLAB_MEX_FILE    
	/* Disable the gate first */
	Drv8301_DisableGate();

	/* Delay for >20us to make sure DRV8301 to perform a full reset */
	DELAY_US(25);

	/* Enable DRV8301 gate driver */
	Drv8301_EnableGate();

	/* Wait for DRV8301 to get ready. This IC needs 5ms-10ms to be stable after EN_GATE is set to HIGH */
	DELAY_US(7000);
#endif    
}


/*
 * Function name	: Drv8301_WriteRegister
 * Description		:
 * Input			:
 * Output			:
 */
uint16_T Drv8301_WriteRegister(uint16_T Register, uint16_T Value)
{
#ifndef MATLAB_MEX_FILE    
	DATA_TX_T   TxData;
	DATA_RX_T   RxData;


	TxData.all = 0x0000;
	RxData.all = 0x0000;

	TxData.bit.RWAccess = DRV8301_REG_ACCESS_WRITE;
	TxData.bit.Address	= Register;
	TxData.bit.Data		= Value & 0x03FF;
	Drv8301_Send(TxData.all, &RxData.all);

	return ((RxData.bit.Fault == 0) ? DRV8301_NOERROR : DRV8301_RWFAIL);
#else
    return DRV8301_NOERROR;
#endif
}


/*
 * Function name	: Drv8301_ReadRegister
 * Description		:
 * Input			:
 * Output			:
 */
uint16_T Drv8301_ReadRegister(uint16_T Register, uint16_T* Value)
{
#ifndef MATLAB_MEX_FILE    
	DATA_TX_T   TxData;
	DATA_RX_T   RxData;


	TxData.all = 0x0000;
	RxData.all = 0x0000;

	TxData.bit.RWAccess = DRV8301_REG_ACCESS_READ;
	TxData.bit.Address  = Register;
	TxData.bit.Data	    = 0x0000;
	Drv8301_Send(TxData.all, &RxData.all);

	if (RxData.bit.Fault == 0)
	{
		*Value = RxData.bit.Data;
		return DRV8301_NOERROR;
	}
	else
	{
		return DRV8301_RWFAIL;
	}
#else
    return DRV8301_NOERROR;
#endif    
}


/*
 * Function name	: Drv8301_Config
 * Description		:
 * Input			:
 * Output			:
 */
uint16_T Drv8301_Config(uint16_T* IsenseU_Offset, uint16_T* IsenseV_Offset)
{
#ifndef MATLAB_MEX_FILE    
	uint16_T Value;
    uint16_T Result;
    
    
	/* Perform a DRV8301 full reset */
	Drv8301_FullReset();

	/* Make sure the FAULT bit is not set in Status Register 1 */
    Result = Drv8301_ReadRegister(DRV8301_STATUS1_REG, &Value);
	if (Result != DRV8301_NOERROR)
        return Result;
    else if ((Value & DRV8301_STATUS1_REG_FAULT_BIT) == 1)
		return DRV8301_FAULT;


	/* Configure Control Register 1 */
	Drv8301.Ctrl1Reg.bit.GATE_CURRENT = DRV8301_GATE_CURRENT_1A7;
	Drv8301.Ctrl1Reg.bit.GATE_RESET   = DRV8301_GATE_NORMAL;
	Drv8301.Ctrl1Reg.bit.PWM_MODE	  = DRV8301_PWM_MODE_6PWM;
	Drv8301.Ctrl1Reg.bit.OCP_MODE	  = DRV8301_OCP_CURRENT_LIMIT;
	Drv8301.Ctrl1Reg.bit.OC_ADJ_SET   = DRV8301_OCADJ_0_060;
	Result = Drv8301_WriteRegister(DRV8301_CONTROL1_REG, Drv8301.Ctrl1Reg.all);
    if (Result != DRV8301_NOERROR)
        return Result;

	/* Configure Control Register 2 */
	Drv8301.Ctrl2Reg.bit.OCTW_MODE	  = DRV8301_OCTW_REPORT_OT_AND_OC;
	Drv8301.Ctrl2Reg.bit.GAIN		  = DRV8301_GAIN_80V_V;
	Drv8301.Ctrl2Reg.bit.DC_CAL_CH1	  = DRV8301_DCCAL_LOAD_CONNECT;
	Drv8301.Ctrl2Reg.bit.DC_CAL_CH2   = DRV8301_DCCAL_LOAD_CONNECT;
	Drv8301.Ctrl2Reg.bit.OC_TOFF	  = DRV8301_OC_TOFF_CYCLE_BY_CYCLE;
	Result = Drv8301_WriteRegister(DRV8301_CONTROL2_REG, Drv8301.Ctrl2Reg.all);
    if (Result != DRV8301_NOERROR)
        return Result;    

	/* Verify the value written to Control Register 1 */
	Result = Drv8301_ReadRegister(DRV8301_CONTROL1_REG, &Value);
	if (Result != DRV8301_NOERROR)
        return Result;
    else if (Value != Drv8301.Ctrl1Reg.all)
		return DRV8301_RWFAIL;

	/* Verify the value written to Control Register 2 */
	Result = Drv8301_ReadRegister(DRV8301_CONTROL2_REG, &Value);
	if (Result != DRV8301_NOERROR)
        return Result;
    else if (Value != Drv8301.Ctrl2Reg.all)
		return DRV8301_RWFAIL;

	/* Read the Status Register 1 */
	Result = Drv8301_ReadRegister(DRV8301_STATUS1_REG, &Drv8301.Stat1Reg.all);
	if (Result != DRV8301_NOERROR)
        return Result;

	/* Read the Status Register 2 */
	Result = Drv8301_ReadRegister(DRV8301_STATUS2_REG, &Drv8301.Stat2Reg.all);
	if (Result != DRV8301_NOERROR)
        return Result;      
    
    /* Calibrate to DC offset */
    Drv8301_CalibrateDcOffset (IsenseU_Offset, IsenseV_Offset);

	/* Return the DRV8301 handle if no errors, otherwise return NULL */
	return DRV8301_NOERROR;
#else
    return DRV8301_NOERROR;
#endif    
}


/*
 * Function name	: Drv8301_CheckFault
 * Description		:
 * Input			:
 * Output			:
 */
uint16_T Drv8301_CheckFault(void)
{
#ifndef MATLAB_MEX_FILE    
	if (Drv8301_IsFaultDetected())
		return DRV8301_FAULT;
	else if (Drv8301_IsOctwDetected())
        return DRV8301_OCTW;
    else
		return DRV8301_NOERROR;
#else
    return DRV8301_NOERROR;
#endif
}
